﻿using System;
using System.IO;
using System.Net;
using HtmlAgilityPack;
using Pospa.NET.WebPageTestApi.DataObjects;
using Pospa.NET.WebPageTestApi.Interfaces;

namespace Pospa.NET.WebPageTestApi
{
	public class ExtendedWebPageTestWorker : IExtendedWebPageTest
	{
		#region Constants

		private const string UserAgent =
			"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17";

		private const string Accept = @"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
		private const string AcceptCharset = "utf-8";
		private const string CacheControl = "no-cache";

		#endregion

		public IExtendedTestResult GetTestResult(IBaseTestRequest testRequest)
		{
			ExtendedTestResult result = new ExtendedTestResult();
			WebRequest request = CreateAndInitHttpWebRequest(testRequest.Url);
			using (WebResponse response = request.GetResponse())
			{
				result.WebServer = !string.IsNullOrEmpty(response.Headers["Server"]) ? response.Headers["Server"] : string.Empty;
				HtmlDocument html = new HtmlDocument();
				html.Load(response.GetResponseStream());
				result.HtmlSize = html.DocumentNode.OuterHtml.Length;
				ChceckCss(testRequest.Url, html, result);
				CheckScripts(testRequest.Url, html, result);
				CheckImages(testRequest.Url, html, result);
				CheckFavicon(testRequest.Url, html, result);
			}
			return result;
		}

		private void CheckFavicon(Uri baseUrl, HtmlDocument html, ExtendedTestResult result)
		{
			HtmlNodeCollection links = html.DocumentNode.SelectNodes("//link[@rel='icon']");
			result.HasFaviconLink = links != null && links.Count == 1;
			result.HasFaviconIcon = GetFileSize(baseUrl, "favicon.ico") > 0;
		}

		#region Private methods

		private static WebRequest CreateAndInitHttpWebRequest(Uri url)
		{
			WebRequest request = WebRequest.Create(url);
			if (request is HttpWebRequest)
			{
				((HttpWebRequest) request).KeepAlive = true;
				((HttpWebRequest) request).AllowAutoRedirect = true;
				((HttpWebRequest) request).AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
				((HttpWebRequest) request).Accept = Accept;
				((HttpWebRequest) request).UserAgent = UserAgent;
				request.Headers.Add("Accept-Charset", AcceptCharset);
				request.Headers.Add(HttpRequestHeader.CacheControl, CacheControl);
				CookieContainer cookieContainer = new CookieContainer();
				((HttpWebRequest) request).CookieContainer = cookieContainer;
			}
			request.Timeout = 10000;

			return request;
		}

		private static void CheckImages(Uri baseUrl, HtmlDocument html, ExtendedTestResult result)
		{
			result.ImagesBaseAddrMatch = true;
			HtmlNodeCollection links = html.DocumentNode.SelectNodes("//img[not(@width)]");
			if (links != null)
			{
				result.ImagesNoDimensionCount = links.Count;
			}
			links = html.DocumentNode.SelectNodes("//img");
			if (links == null)
			{
				result.ImageCount = 0;
				result.ImageSize = 0;
				return;
			}
			result.ImageCount = links.Count;
			foreach (HtmlNode link in links)
			{
				Uri imgUri;
				string imageSrc = link.GetAttributeValue("src", string.Empty);
				if (Uri.TryCreate(imageSrc, UriKind.Absolute, out imgUri) && imgUri.Host != baseUrl.Host)
				{
					result.ImagesBaseAddrMatch = false;
				}
				result.ImageSize += GetFileSize(baseUrl, imageSrc);
			}
		}

		private static void CheckScripts(Uri baseUrl, HtmlDocument html, ExtendedTestResult result)
		{
			HtmlNodeCollection links = html.DocumentNode.SelectNodes("//script[@src]");
			HtmlNodeCollection inlineScripts = html.DocumentNode.SelectNodes("//script[not(@src)]");
			result.InlineScriptCount = inlineScripts != null ? inlineScripts.Count : 0;
			if (links == null)
			{
				if (inlineScripts != null)
					result.ScriptSize = 0;
				return;
			}
			result.ScriptCount = links.Count;
			foreach (HtmlNode link in links)
			{
				result.ScriptSize += GetFileSize(baseUrl, link.GetAttributeValue("src", string.Empty));
			}
		}

		private static void ChceckCss(Uri baseUrl, HtmlDocument html, ExtendedTestResult result)
		{
			HtmlNodeCollection links = html.DocumentNode.SelectNodes("//link[@rel='stylesheet']");
			HtmlNodeCollection styles = html.DocumentNode.SelectNodes("//style");
			result.InlineCssCount = styles != null ? styles.Count : 0;
			if (links == null)
			{
				result.CssCount = 0;
				result.CssSize = 0;
				return;
			}
			result.CssCount = links.Count;
			foreach (HtmlNode link in links)
			{
				result.CssSize += GetFileSize(baseUrl, link.GetAttributeValue("href", string.Empty));
			}
		}

		private static long GetFileSize(Uri baseUrl, string href)
		{
			string url = WebUtility.HtmlDecode(href);
			Uri fileUri;
			if (!Uri.TryCreate(url, UriKind.Absolute, out fileUri))
			{
				fileUri = new Uri(baseUrl, url);
			}
			WebRequest request = CreateAndInitHttpWebRequest(fileUri);

			WebResponse response = null;
			try
			{
				response = request.GetResponse();
				if (response.ContentLength != -1)
				{
					return response.ContentLength;
				}
				else
				{
					using (StreamReader sr = new StreamReader(response.GetResponseStream()))
					{
						return sr.ReadToEnd().Length;
					}
				}
			}
			catch (WebException)
			{
				return 0;
			}
			finally
			{
				if (response != null)
				{
					response.Close();
					response.Dispose();
				}
			}
		}

		#endregion
	}
}